﻿/**
 * Copyright (C) 2012-2013 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Binder;
using NGuice.Inject.Spi;
using NGuice.Inject.Internal.Util;

namespace NGuice.Inject.Internal
{
    /// <summary>
    /// 绑定一个常数
    /// </summary>
    public sealed class ConstantBindingBuilderImpl<T> : AbstractBindingBuilder<T>, IAnnotatedConstantBindingBuilder, IConstantBindingBuilder
    {
        public ConstantBindingBuilderImpl(IBinder binder, IList<IElement> elements, object source)
            : base(binder, elements, source, (Key<T>)AbstractBindingBuilder<T>.NULL_KEY)
        {
        }

        public IConstantBindingBuilder AnnotatedWith<A>()
            where A : Attribute
        {
            return AnnotatedWith(typeof(A));
        }

        public IConstantBindingBuilder AnnotatedWith(Type attributeType)
        {
            AnnotatedWithInternal(attributeType);
            return this;
        }

        public IConstantBindingBuilder AnnotatedWith(Attribute attribute)
        {
            AnnotatedWithInternal(attribute);
            return this;
        }

        public void To(string value)
        {
            ToConstant(typeof(string), value);
        }

        public void To(int value)
        {
            ToConstant(typeof(int), value);
        }

        public void To(long value)
        {
            ToConstant(typeof(long), value);
        }

        public void To(bool value)
        {
            ToConstant(typeof(bool), value);
        }

        public void To(double value)
        {
            ToConstant(typeof(double), value);
        }

        public void To(float value)
        {
            ToConstant(typeof(float), value);
        }

        public void To(short value)
        {
            ToConstant(typeof(short), value);
        }

        public void To(char value)
        {
            ToConstant(typeof(char), value);
        }

        public void To(byte value)
        {
            ToConstant(typeof(byte), value);
        }

        public void To(Type value)
        {
            ToConstant(typeof(Type), value);
        }

        public void To(Enum value)
        {
            ToConstant(value.GetType(), value);
        }

        /// <summary>
        /// type必须为泛型定义的T类型，instance必须为T类型的对象，否则将抛出强转异常
        /// </summary>
        /// <param name="type"></param>
        /// <param name="instance"></param>
        private void ToConstant(Type type, object instance)
        {
            // this type will define T, so these assignments are safe
            //Class<T> typeAsClassT = (Class<T>) type;
            T instanceAsT = (T)instance;

            if (KeyTypeIsSet())
            {
                binder.AddError(CONSTANT_VALUE_ALREADY_SET);
                return;
            }

            BindingImpl<T> super = base.Binding;
            Key<T> key;
            if (super.Key.Attribute != null)
            {
                key = Key<T>.Get(super.Key.Attribute);
            }
            else if (super.Key.AttributeType != null)
            {
                key = Key<T>.Get(super.Key.AttributeType);
            }
            else
            {
                key = Key<T>.Get();
            }

            if (instanceAsT == null)
            {
                binder.AddError(BINDING_TO_NULL);
            }

            Binding = (new InstanceBindingImpl<T>(super.Source, key, super.Scoping, ImmutableSet<InjectionPoint>.Of(), instanceAsT));
        }
    }
}
